//Scheduler (Codename "Hendrik Antoon Lorentz")
#include "scheduler.h"
#include "memory_management.h"
#include "lib.h"
#include "serial.h"
#include "interrupt_handler.h"

int freshRoundRobin = 1;

//Prozesskontrollblöcke
pcb* pcbArray;

int currentProcNumber;
int maximumProcNumber;
// zuweisen des Speichers über Kmalloc für den Prozesskontrollblock

//init_PCB erstellt den PCB für n Prozesse und initialisiert Round Robin
void init_PCB(int n){
	init_RoundRobin();
	pcbArray = kmalloc(n*sizeof(pcb));
}

//initialisiert einen Prozess
void init_Process(long processAddress,long procNumber){
  
   pcb* temp = (pcb*)((long)pcbArray + procNumber*sizeof(pcb));
   
   //Set Status Register Bits to activate the user mode
   temp->armRegister[0] = USRMODE;
   
   int i;
   for(i = 1; i <=13; i++){
      temp->armRegister[i] = i-1;//R0 bis R12 mit 0 initialisieren
   }
   //Stackpointer
   long stackSize = 1000*sizeof(long);
   long* stack = kmalloc(stackSize);
   temp->armRegister[14] = (long)stack+stackSize;
   //Linkregister
   temp->armRegister[15] = 14; //kein Rücksprung aus dem Prozess
   //Program Counter
   temp->armRegister[16] = (long)processAddress; 

   maximumProcNumber++;
}

//gibt alle Register eines Prozesses aus
void showRegisters(long procNumber){
  pcb* temp = (pcb*)((long)pcbArray + procNumber*sizeof(pcb));
  int i;
  for(i = 0; i<=16; i++){
      printf("R%d: %x\n",i,temp->armRegister[i]);
  }
}

void init_RoundRobin(){
      currentProcNumber = 0;
      maximumProcNumber = 0;
}


//gibt den pcb-Pointer des Prozesses zurück, der nach dem Runden Robin an der Reihe ist
void roundRobin(long stackPointer){
  /**
 * memcpy kopiert die Bytes eines Speicherbereiches in einen zweiten 
 * Speicherbereich.
 *
 * @param t : Zieladresse
 * @param src : Quelladresse
 * @param size : Anzahl zu kopierender Bytes
 */
  //write("Starte Prozesswechsel\n");
  //wenn der erste Interrupt kommt, wurde gar kein Prozess unterbrochen, sondern der kernel befindet sich noch in der Initialierungsphase -> keinen Prozess speichern
  //beim ersten Ausführen bleibt currentProcNumber auf 0, dadurch wird der zuerst registrierte Prozess auch als erstes gestartet
	if(!freshRoundRobin){
		  pcb* targetAddress = (pcb*)((long)pcbArray + currentProcNumber*sizeof(pcb)); //ganz klar
		  memcpy((void*) targetAddress,(const void*) stackPointer, sizeof(pcb)) ;
		  targetAddress->armRegister[15] -= 4;
		  //showRegisters(currentProcNumber);
		  //printf("\n");
		  
		  if(currentProcNumber == maximumProcNumber-1){ //springe wieder zum ersten Element wenn alle Prozesse durchlaufen sind
	      		currentProcNumber = 0;
		  }
		  else{
		    currentProcNumber++;
		  }  
	}
	else{
		freshRoundRobin = 0;
	}
  	
  	  const void* nextProcSourceAddress = (const void*)((long)pcbArray + currentProcNumber*sizeof(pcb)); //ganz klar
	  memcpy( (void*) stackPointer, nextProcSourceAddress, sizeof(pcb)) ;
	  handler();
	
}
  

